package cc.eddic.practice.concurrency.impl;

import cc.eddic.practice.concurrency.LogItem;
import cc.eddic.practice.concurrency.LogParser;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static cc.eddic.practice.concurrency.ExecutorServiceUtil.shutdown;
import static java.lang.Thread.sleep;

@Slf4j
@Component
@Qualifier("LogItemParserWithFutureCxyImpl2")
public class LogItemParserWithFutureCxyImpl2 implements LogParser {
    @Override
    public Map<String, List<LogItem>> parse(String folderName) {
        Map<String, List<LogItem>> resultMap = new ConcurrentHashMap<>();
        ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

        try (Stream<Path> walk = Files.walk(Paths.get(folderName))) {
            List<Future<Model>> futures = walk
                    .filter(Files::isRegularFile)
                    .filter(path -> path.getFileName().toString().endsWith(".log"))
                    .map(logFile -> executorService.submit(() -> parseFile(logFile)))
                    .collect(Collectors.toList());

            allOf(futures);

            futures.stream()
                    .map(this::getFuture)
                    .filter(Objects::nonNull)
                    .forEach(model -> resultMap.put(model.getPath().toString(), model.getItems()));

        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        } finally {
            shutdown(executorService);
        }

        return resultMap;
    }

    private <T> void allOf(final List<Future<T>> futures) throws InterruptedException {
        while (true) {
            int cnt = 0;
            for (Future<?> future : futures) {
                if (future.isDone()) {
                    cnt++;
                }
            }
            sleep(10);
            if (cnt >= futures.size()) {
                break;
            }
        }
    }

    private Model getFuture(Future<Model> future) {
        try {
            return future.get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
            return null;
        }
    }

    private Model parseFile(Path path) {
        try (val lines = Files.lines(path)) {
            LogItemParserCxyImpl logItemParser = new LogItemParserCxyImpl();
            List<LogItem> items = lines.map(logItemParser::parse)
                    .filter(Optional::isPresent)
                    .map(Optional::get)
                    .collect(Collectors.toList());
            return new Model(path, items);
        } catch (IOException exception) {
            exception.printStackTrace();
            return new Model(path);
        }
    }

    @Getter
    protected static class Model {
        public Model(Path path) {
            this(path, new ArrayList<>());
        }

        public Model(Path path, List<LogItem> items) {
            this.path = path;
            this.items = items;
        }

        private final Path path;
        private final List<LogItem> items;
    }


}








